#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <sys/uio.h>
#include <pthread.h>
#include <sched.h>
#include "queue.h"

/* Déclaration d'une fonction définie plus tard.  Le C aime bien ça. */

static void* thread_main(void *q);

/* On va pas se fatiguer, on arrête dès qu'il y a une erreur. */

static void
fatal(const char *message)
{
    perror(message);
    exit(1);
}

/* Le même, mais qui prend un code explicite plutôt que consulter errno. */

static void
fatal_code(const char *message, int code)
{
    fprintf(stderr, "%s: %s\n", message,
            code >= 0 && code < sys_nerr ? sys_errlist[code] : "Unkown error");
}

/* Ben ouais, ça n'existe pas en C. */

static int
min(int i, int j)
{
    return i <= j ? i : j;
}

/* gere les options*/

int init_opt(int argc, char* argv[])
{
  int optch, retval;
  extern int opterr;  
  
  char format[] = "cd";  
  
  opterr = 1;  
  
  while ((optch = getopt(argc, argv, format)) != -1)  
    switch (optch) 
      {  
      case 'c':  
	opt = 1;
	retval = 0;
	break;  
      case 'd':  
	opt = 2;
	retval = 0;
	break;  
      default :
	opt = 0;
	retval = -1;
      }  
  
  return retval;
}

int
main(int argc, char* argv[])
{
    int i, rc, retopt;
    struct queue *queue;
    pthread_t thread;
    /* pthread_t thr; */

    retopt = init_opt(argc,argv);
    if( retopt < 0){
      perror("option non existante");
      exit(1);
    }

    queue = queue_new(17);
    if(queue == NULL)
        fatal("new_queue");

    /* Crée un thread qui exécute thread_main(queue). */
    rc = pthread_create(&thread, NULL, thread_main, (void*)queue);
    if(rc != 0)
        fatal_code("pthread_create", rc);
    /*
     
    rc = pthread_create(&thr, NULL, thread_main, (void*)queue);
    if(rc != 0)
        fatal_code("pthread_create2", rc);
    */
    printf("Test non-bloquant.\n");
    for(i = 0; i < 100; i++) {
    again:
        rc = enqueue(queue, &i, sizeof(i), 0);
        if(rc < 0) {
            if(errno == EAGAIN) {
                sched_yield();
                goto again;
            }
            fatal("enqueue");
        }
    }
    sleep(1);

    printf("Test bloquant.\n");
    for(i = 0; i < 100; i++) {
        rc = enqueue(queue, &i, sizeof(i), 1);
        if(rc < 0)
            fatal("enqueue");
    }
    sleep(1);

    
    printf("Test multiple.\n");
    i = 0;
    while(i < 100) {
        int j = i + 1, k = i + 2, l = i + 3;
        struct iovec iov[4];
        iov[0].iov_base = &i; iov[0].iov_len = sizeof(i);
        iov[1].iov_base = &j; iov[1].iov_len = sizeof(j);
        iov[2].iov_base = &k; iov[2].iov_len = sizeof(k);
        iov[3].iov_base = &l; iov[3].iov_len = sizeof(l);
        rc = enqueue_multiple(queue, iov, min(97 - i, 4), 1);
        if(rc < 0)
            fatal("enqueue_multiple");
        assert(rc > 0);
        i += rc;
    }

    rc = pthread_join(thread, NULL);
    if(rc != 0)
        fatal_code("pthread_join", rc);
    /* 
    rc = pthread_join(thr, NULL);
    if(rc != 0)
        fatal_code("pthread2_join", rc);
    */

    queue_destroy(queue);

    printf("Option val : %d\n",opt);
    
    return 0;
}

/* La fonction exécutée par le thread. */

static void*
thread_main(void *q)
{
 
  struct queue *queue = (struct queue *)q;
  int i;
   
    printf("---> Inside thread method !\n");
    for(i = 0; i < 100; i++) {
        int val, rc;
    again2:
        rc = dequeue(queue, &val, sizeof(val), 0);
        if(rc < 0) {
            if(errno == EAGAIN) {
                sched_yield();
                goto again2;
            }
            fatal("dequeue");
        }
	/*	printf("rc = %d et val = %d et i = %d\n",rc,val,i);*/
        assert(rc == sizeof(val) && val == i);
    }
    printf("Test non-bloquant terminé.\n");
    

    for(i = 0; i < 100; i++) {
        int val, rc;
        rc = dequeue(queue, &val, sizeof(val), 1);
        if(rc < 0)
            fatal("dequeue");
        assert(rc == sizeof(val) && val == i);
    }
    printf("Test bloquant terminé.\n");

    
    i = 0;
    while(i < 100) {
        int rc, j = i + 1, k = i + 2, l = i + 3;
        struct iovec iov[4];
        iov[0].iov_base = &i; iov[0].iov_len = sizeof(i);
        iov[1].iov_base = &j; iov[1].iov_len = sizeof(j);
        iov[2].iov_base = &k; iov[2].iov_len = sizeof(k);
        iov[3].iov_base = &l; iov[3].iov_len = sizeof(l);
        rc = dequeue_multiple(queue, iov, min(4, 97 - i), 1);
        if(rc < 0)
            fatal("enqueue_multiple");
        assert(rc > 0);
        i += rc;
    }
    printf("Test multiple terminé.\n");
    

    pthread_exit(NULL);
}
